package br.org.isvi.interpretador.portugol;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PushbackReader;
import java.util.ArrayList;
import java.util.HashMap;

import br.org.isvi.interpretador.portugol.exception.SemanticErrorException;
import br.org.isvi.interpretador.portugol.sablecc.analysis.DepthFirstAdapter;
import br.org.isvi.interpretador.portugol.sablecc.lexer.Lexer;
import br.org.isvi.interpretador.portugol.sablecc.lexer.LexerException;
import br.org.isvi.interpretador.portugol.sablecc.node.AInteiroType;
import br.org.isvi.interpretador.portugol.sablecc.node.ALogicoType;
import br.org.isvi.interpretador.portugol.sablecc.node.ARealType;
import br.org.isvi.interpretador.portugol.sablecc.node.ATextoType;
import br.org.isvi.interpretador.portugol.sablecc.node.AVariablesDeclaration;
import br.org.isvi.interpretador.portugol.sablecc.node.Node;
import br.org.isvi.interpretador.portugol.sablecc.node.PType;
import br.org.isvi.interpretador.portugol.sablecc.node.Start;
import br.org.isvi.interpretador.portugol.sablecc.node.TIdentifier;
import br.org.isvi.interpretador.portugol.sablecc.node.Token;
import br.org.isvi.interpretador.portugol.sablecc.parser.Parser;
import br.org.isvi.interpretador.portugol.sablecc.parser.ParserException;
import br.org.isvi.interpretador.portugol.types.Kind;
import br.org.isvi.interpretador.portugol.types.Symbol;
import br.org.isvi.interpretador.portugol.types.Variable;
import br.org.isvi.interpretador.portugol.types.VariableType;

public class SabbleAnalysis extends DepthFirstAdapter {

	int line, pos;
	HashMap<String, Variable> varsTable = new HashMap<String, Variable>();
	ArrayList<String> varslist = new ArrayList<String>();
	ArrayList<Symbol> elements = new ArrayList<Symbol>();
	/**
	 * Controle do leia por causa dos tipos.
	 */
	boolean leiaStatment = false;
	boolean def = false;
	
	
	@Override
	public void defaultCase(Node node) {
		if (node instanceof Token) {
			Token t = (Token) node;
			pos = t.getPos();
			line = t.getLine();
		}
	}

	public int getLastLine() {
		return line;
	}

	public int getLastPos() {
		return pos;
	}
	
	
	public void inType(PType node) {
		if (leiaStatment)// TODO olhar isso!
			return;
		
		for (String name : varslist) {
			if(varsTable.containsKey(name))
				throw new SemanticErrorException("Redeclaração de Variável: " + name);
			
			varsTable.put(name, SymbolsFactory.createVariable(name, getVariableTypeFor(node)));
		}

		varslist = new ArrayList<String>();// REINICIA LISTAGEM
	}
	
	@Override
	public void inAVariablesDeclaration(AVariablesDeclaration node) {
		def = true;
	}

	@Override
	public void outAVariablesDeclaration(AVariablesDeclaration node) {
		def = false;
	}

	@Override
	public void caseTIdentifier(TIdentifier node) {
		String name;

		name = node.getText();

		if (def) {
			varslist.add(name);
		} else {
			if(!varsTable.containsKey(name))
				throw new SemanticErrorException("Variável: " + name + " não foi declarada!");
			
			elements.add(varsTable.get(name));
		}
	}

	@Override
	public void inATextoType(ATextoType node) {
		inType(node);
	}

	@Override
	public void inARealType(ARealType node) {
		inType(node);
	}

	@Override
	public void inALogicoType(ALogicoType node) {
		inType(node);
	}

	@Override
	public void inAInteiroType(AInteiroType node) {
		inType(node);
	}
	
	public static void main(String[] args) {

		InterpreterResolver.instance.setResolver(new BasicResolver());
		SabbleAnalysis transformer = new SabbleAnalysis();
		Lexer lex = null;
		Parser parser = null;
		try {
			Start tree;

			File file = new File("teste.por");

			PushbackReader pushbackReader = new PushbackReader(new FileReader(
					file), 10000);

			lex = new Lexer(pushbackReader);
			parser = new Parser(lex);

			tree = parser.parse();
			tree.apply(transformer);

		} catch (SemanticErrorException e) {
			InterpreterResolver.instance.performeHandlingError(e.getError(),
					transformer.getLastLine(), transformer.getLastPos());
		} catch (ParserException e) {
			String msg = e.getMessage().replaceAll("expecting:", "esperando:");
			InterpreterResolver.instance.performeHandlingError(msg, e
					.getToken().getLine(), e.getToken().getPos());
		} catch (LexerException e) {
			String msg = "Na [linha, coluna] = "
					+ e.getMessage().replaceAll("Unknown token",
							"forma inválida");
			InterpreterResolver.instance.performeHandlingError(msg, -1, -1);
		} catch (IOException e) {
			InterpreterResolver.instance.performeHandlingError(
					"Arquivo não existe!", 0, 0);
		} catch (Throwable e) {
			InterpreterResolver.instance.performeHandlingError(
					"Erro desconhecido! " + e.getCause(), 0, 0);
			e.printStackTrace();
		}

	}

	static VariableType getVariableTypeFor(PType type) {
		VariableType ret = null;

		if (type instanceof AInteiroType)
			ret = VariableType.inteiro;

		if (type instanceof ATextoType)
			ret = VariableType.literal;

		if (type instanceof ALogicoType)
			ret = VariableType.booleano;

		if (type instanceof ARealType)
			ret = VariableType.real;

		return ret;
	}

	static Kind getInstaceKindFor(PType type) {
		Kind ret = null;

		if (type instanceof AInteiroType)
			ret = Kind.INTEIRO;

		if (type instanceof ATextoType)
			ret = Kind.TEXTO;

		if (type instanceof ALogicoType)
			ret = Kind.BOOLEANO;

		if (type instanceof ARealType)
			ret = Kind.REAL;

		return ret;
	}
}
